﻿// -----------------------------------------------------------------------
// <copyright file="Filtering.cs" company="Image Processing Course Project">
// Created 13/12/2011
// </copyright>
// -----------------------------------------------------------------------

namespace ImageProcessing.IntermediateClasses
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using ImageProcessing.Utilities;
    
    /// <summary>
    /// Class for image filtering methods
    /// </summary>
    public class Filtering
    {
        /// <summary>
        /// Original Bitmap Image
        /// </summary>
        private Bitmap originalImage;

        /// <summary>
        /// Instance of class NeighbourhoodOperations
        /// </summary>
        private NeighbourhoodOperations operations;

        /// <summary>
        /// Size of filering window
        /// </summary>
        private int maskSize;

        /// <summary>
        /// ColorsStruct Buffer to carry image color values
        /// </summary>
        private Colour.ColorsStruct[,] buffer;

        /// <summary>
        /// Initializes a new instance of the Filtering class
        /// </summary>
        /// <param name="oldImage">Bitmap original image</param>
        /// <param name="maskSize">Size of filtering window</param>
        public Filtering(Bitmap oldImage, int maskSize)
        {
            this.originalImage = oldImage;
            this.maskSize = maskSize;
            this.operations = new NeighbourhoodOperations(this.originalImage, this.maskSize);
        }

        /// <summary>
        /// Type 0: Minimum Filter
        /// Type 1: Maximum Filter
        /// Type 2: Median Filter
        /// Type 3 :Midpoint Filter
        /// </summary>
        /// <param name="type">Type of filter</param>
        /// <returns>Bitmap filtered Image</returns>
        public Bitmap MinMaxMedianMidpoint(int type)
        {
            this.buffer = this.operations.Padding(this.originalImage);
            Bitmap resultedImage = new Bitmap(this.originalImage.Width, this.originalImage.Height);
            int[,] mixedColorBits = new int[this.originalImage.Width + this.maskSize - 1, this.originalImage.Height + this.maskSize - 1];
            string redValue, greenValue, blueValue;
            try
            {
                for (int i = 0; i < this.originalImage.Width + this.maskSize - 1; i++)
                {
                    for (int j = 0; j < this.originalImage.Height + this.maskSize - 1; j++)
                    {
                        redValue = Convert.ToString(this.buffer[i, j].Red, 2);
                        greenValue = Convert.ToString(this.buffer[i, j].Green, 2);
                        blueValue = Convert.ToString(this.buffer[i, j].Blue, 2);

                        if (redValue.Length < 8)
                        {
                            int num = 8 - redValue.Length;
                            for (int m = 0; m < num; m++)
                            {
                                redValue = "0" + redValue;
                            }
                        }

                        if (greenValue.Length < 8)
                        {
                            int num = 8 - greenValue.Length;
                            for (int m = 0; m < num; m++)
                            {
                                greenValue = "0" + greenValue;
                            }
                        }

                        if (blueValue.Length < 8)
                        {
                            int num = 8 - blueValue.Length;
                            for (int m = 0; m < num; m++)
                            {
                                blueValue = "0" + blueValue;
                            }
                        }

                        string temp = string.Empty;
                        for (int k = 0; k < 8; k++)
                        {
                            temp += redValue[k];
                            temp += greenValue[k];
                            temp += blueValue[k];
                        }

                        mixedColorBits[i, j] = Convert.ToInt32(temp, 2);
                    }
                }

                List<IndexBitmixColor> sortList = new List<IndexBitmixColor>();
                Noise.Index tempIndex;
                IndexBitmixColor indexBitmixObject;
                for (int i = 0; i < this.originalImage.Width; i++)
                {
                    for (int j = 0; j < this.originalImage.Height; j++)
                    {
                        sortList.Clear();
                        for (int k = 0; k < this.maskSize; k++)
                        {
                            for (int l = 0; l < this.maskSize; l++)
                            {
                                indexBitmixObject = new IndexBitmixColor();
                                tempIndex.X = i + k;
                                tempIndex.Y = j + l;
                                indexBitmixObject.Index = tempIndex;
                                indexBitmixObject.BitmixColor = mixedColorBits[i + k, j + l];
                                sortList.Add(indexBitmixObject);
                            }
                        }

                        sortList.Sort();
                        int red, green, blue;
                        switch (type)
                        {
                            case 0:
                                red = this.buffer[sortList[0].Index.X, sortList[0].Index.Y].Red;
                                green = this.buffer[sortList[0].Index.X, sortList[0].Index.Y].Green;
                                blue = this.buffer[sortList[0].Index.X, sortList[0].Index.Y].Blue;
                                resultedImage.SetPixel(i, j, Color.FromArgb(red, green, blue));
                                break;
                            case 1:
                                red = this.buffer[sortList[sortList.Count - 1].Index.X, sortList[sortList.Count - 1].Index.Y].Red;
                                green = this.buffer[sortList[sortList.Count - 1].Index.X, sortList[sortList.Count - 1].Index.Y].Green;
                                blue = this.buffer[sortList[sortList.Count - 1].Index.X, sortList[sortList.Count - 1].Index.Y].Blue;
                                resultedImage.SetPixel(i, j, Color.FromArgb(red, green, blue));
                                break;
                            case 2:
                                if (sortList.Count % 2 != 0)
                                {
                                    red = this.buffer[sortList[sortList.Count / 2].Index.X, sortList[sortList.Count / 2].Index.Y].Red;
                                    green = this.buffer[sortList[sortList.Count / 2].Index.X, sortList[sortList.Count / 2].Index.Y].Green;
                                    blue = this.buffer[sortList[sortList.Count / 2].Index.X, sortList[sortList.Count / 2].Index.Y].Blue;
                                }
                                else
                                {
                                    red = (this.buffer[sortList[sortList.Count / 2].Index.X, sortList[sortList.Count / 2].Index.Y].Red + buffer[sortList[(sortList.Count / 2) + 1].Index.X, sortList[(sortList.Count / 2) + 1].Index.Y].Red) / 2;
                                    green = (this.buffer[sortList[sortList.Count / 2].Index.X, sortList[sortList.Count / 2].Index.Y].Green + buffer[sortList[(sortList.Count / 2) + 1].Index.X, sortList[(sortList.Count / 2) + 1].Index.Y].Green) / 2;
                                    blue = (this.buffer[sortList[sortList.Count / 2].Index.X, sortList[sortList.Count / 2].Index.Y].Blue + buffer[sortList[(sortList.Count / 2) + 1].Index.X, sortList[(sortList.Count / 2) + 1].Index.Y].Blue) / 2;
                                }

                                resultedImage.SetPixel(i, j, Color.FromArgb(red, green, blue));
                                break;
                            case 3:
                                red = (this.buffer[sortList[sortList.Count - 1].Index.X, sortList[sortList.Count - 1].Index.Y].Red + this.buffer[sortList[0].Index.X, sortList[0].Index.Y].Red) / 2;
                                green = (this.buffer[sortList[sortList.Count - 1].Index.X, sortList[sortList.Count - 1].Index.Y].Green + this.buffer[sortList[0].Index.X, sortList[0].Index.Y].Green) / 2;
                                blue = (this.buffer[sortList[sortList.Count - 1].Index.X, sortList[sortList.Count - 1].Index.Y].Blue + this.buffer[sortList[0].Index.X, sortList[0].Index.Y].Blue) / 2;
                                resultedImage.SetPixel(i, j, Color.FromArgb(red, green, blue));
                                break;
                            default:
                                break;
                        }
                    }
                }

                return resultedImage;
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
                throw;
            }
        }

        /// <summary>
        /// Geometric Mean filter of noisy image
        /// </summary>
        /// <returns>Bitmap filtered Image</returns>
        public Bitmap MeanFilter()
        {
            this.buffer = this.operations.Padding(this.originalImage);
            double averageValueRed = 1, averageValueGreen = 1, averageValueBlue = 1;
            Bitmap resultedImage = new Bitmap(this.originalImage.Width, this.originalImage.Height);
            int maskSizeSquared = this.maskSize * this.maskSize;
            try
            {
                for (int i = 0; i < this.originalImage.Width; i++)
                {
                    for (int j = 0; j < this.originalImage.Height; j++)
                    {
                        for (int k = 0; k < this.maskSize; k++)
                        {
                            for (int l = 0; l < this.maskSize; l++)
                            {
                                if (this.buffer[i + k, j + l].Red == 0 && this.buffer[i + k, j + l].Green == 0 && this.buffer[i + k, j + l].Blue == 0)
                                {
                                    this.buffer[i + k, j + l].Red = 1;
                                    this.buffer[i + k, j + l].Green = 1;
                                    this.buffer[i + k, j + l].Blue = 1;
                                }

                                averageValueRed *= Math.Pow(this.buffer[i + k, j + l].Red, (1 / (double)maskSizeSquared));
                                averageValueGreen *= Math.Pow(this.buffer[i + k, j + l].Green, (1 / (double)maskSizeSquared));
                                averageValueBlue *= Math.Pow(this.buffer[i + k, j + l].Blue, (1 / (double)maskSizeSquared));
                            }
                        }

                        resultedImage.SetPixel(i, j, Color.FromArgb((int)averageValueRed, (int)averageValueGreen, (int)averageValueBlue));
                        averageValueRed = 1;
                        averageValueGreen = 1;
                        averageValueBlue = 1;
                    }
                }

                return resultedImage;
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
                throw;
            }
        }

        /// <summary>
        /// Ideal Notch Reject Periodic Filter
        /// </summary>
        /// <param name="ops">Instance of FourierOperations class</param>
        /// <param name="x">Point x coordinate</param>
        /// <param name="y">Point y coordinate</param>
        /// <param name="radius">Radius of notch</param>
        /// <returns>Bitmap image with filter applied to it</returns>
        public Bitmap IdealNotchRejectFilter(FourierOperations ops, int x, int y, int radius)
        {
            int pixelValue = this.originalImage.GetPixel(x, y).R;
            int firstX = (this.originalImage.Width / 2) + x;
            int firstY = (this.originalImage.Height / 2) + y;
            int secondX = (this.originalImage.Width / 2) - x;
            int secondY = (this.originalImage.Height / 2) - y;
            
            double distance1, distance2;
            int red, green, blue;
            try
            {
                for (int i = 0; i < this.originalImage.Height; i++)
                {
                    for (int j = 0; j < this.originalImage.Width; j++)
                    {
                        distance1 = Math.Sqrt(Math.Pow(j - firstX, 2) + Math.Pow(i - firstY, 2));
                        distance2 = Math.Sqrt(Math.Pow(j - secondX, 2) + Math.Pow(i - secondY, 2));
                        if (distance1 > radius && distance2 > radius)
                        {
                        }
                        else
                        {
                            ops.realR[j, i] *= 0;
                            ops.realG[j, i] *= 0;
                            ops.realB[j, i] *= 0;
                            ops.imaginaryR[j, i] *= 0;
                            ops.imaginaryG[j, i] *= 0;
                            ops.imaginaryB[j, i] *= 0;

                            red = this.originalImage.GetPixel(j, i).R * 0;
                            green = this.originalImage.GetPixel(j, i).G * 0;
                            blue = this.originalImage.GetPixel(j, i).B * 0;
                            this.originalImage.SetPixel(j, i, Color.FromArgb(red, green, blue));
                        }
                    }
                }

                return this.originalImage;
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
                throw;
            }
        }

        /// <summary>
        /// Ideal Notch Pass Periodic Filter
        /// </summary>
        /// <param name="ops">Instance of FourierOperations class</param>
        /// <param name="x">Point x coordinate</param>
        /// <param name="y">Point y coordinate</param>
        /// <param name="radius">Radius of notch</param>
        /// <returns>Bitmap image with filter applied to it</returns>
        public Bitmap IdealNotchPassFilter(FourierOperations ops, int x, int y, int radius)
        {
            int pixelValue = this.originalImage.GetPixel(x, y).R;
            int firstX = (this.originalImage.Width / 2) + x;
            int firstY = (this.originalImage.Height / 2) + y;
            int secondX = (this.originalImage.Width / 2) - x;
            int secondY = (this.originalImage.Height / 2) - y;

            double distance1, distance2;
            int red, green, blue;
            try
            {
                for (int i = 0; i < this.originalImage.Height; i++)
                {
                    for (int j = 0; j < this.originalImage.Width; j++)
                    {
                        distance1 = Math.Sqrt(Math.Pow(j - firstX, 2) + Math.Pow(i - firstY, 2));
                        distance2 = Math.Sqrt(Math.Pow(j - secondX, 2) + Math.Pow(i - secondY, 2));
                        if (distance1 > radius && distance2 > radius)
                        {
                            ops.realR[j, i] *= 0;
                            ops.realG[j, i] *= 0;
                            ops.realB[j, i] *= 0;
                            ops.imaginaryR[j, i] *= 0;
                            ops.imaginaryG[j, i] *= 0;
                            ops.imaginaryB[j, i] *= 0;

                            red = this.originalImage.GetPixel(j, i).R * 0;
                            green = this.originalImage.GetPixel(j, i).G * 0;
                            blue = this.originalImage.GetPixel(j, i).B * 0;
                            this.originalImage.SetPixel(j, i, Color.FromArgb(red, green, blue));
                        }
                    }
                }

                return this.originalImage;
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
                throw;
            }
        }

        /// <summary>
        /// Ideal Band Reject Periodic Filter
        /// </summary>
        /// <param name="ops">Instance of FourierOperations class</param>
        /// <param name="distance">Distance between center of band and center of image</param>
        /// <param name="bandwidth">Width of filter band</param>
        /// <returns>Bitmap image with filter applied to it</returns>
        public Bitmap IdealBandRejectFilter(FourierOperations ops, double distance, int bandwidth)
        {
            double distanceX, distanceY, pointDistance;
            int red, green, blue;
            try
            {
                for (int i = 0; i < this.originalImage.Height; i++)
                {
                    distanceY = Math.Abs(i - (this.originalImage.Height / 2));
                    for (int j = 0; j < this.originalImage.Width; j++)
                    {
                        distanceX = Math.Abs(j - (this.originalImage.Width / 2));
                        pointDistance = Math.Sqrt(Math.Pow(distanceX, 2) + Math.Pow(distanceY, 2));
                        if (pointDistance <= distance + (bandwidth / 2) && pointDistance >= distance - (bandwidth / 2))
                        {
                            ops.realR[j, i] *= 0;
                            ops.realG[j, i] *= 0;
                            ops.realB[j, i] *= 0;
                            ops.imaginaryR[j, i] *= 0;
                            ops.imaginaryG[j, i] *= 0;
                            ops.imaginaryB[j, i] *= 0;

                            red = this.originalImage.GetPixel(j, i).R * 0;
                            green = this.originalImage.GetPixel(j, i).G * 0;
                            blue = this.originalImage.GetPixel(j, i).B * 0;
                            this.originalImage.SetPixel(j, i, Color.FromArgb(red, green, blue));
                        }
                    }
                }

                return this.originalImage;
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
                throw;
            }
        }

        /// <summary>
        /// Ideal Band Pass Periodic Filter
        /// </summary>
        /// <param name="ops">Instance of FourierOperations class</param>
        /// <param name="distance">Distance between center of band and center of image</param>
        /// <param name="bandwidth">Width of filter band</param>
        /// <returns>Bitmap image with filter applied to it</returns>
        public Bitmap IdealBandPassFilter(FourierOperations ops, double distance, int bandwidth)
        {
            double distanceX, distanceY, pointDistance;
            int red, green, blue;
            try
            {
                for (int i = 0; i < this.originalImage.Height; i++)
                {
                    distanceY = Math.Abs(i - (this.originalImage.Height / 2));
                    for (int j = 0; j < this.originalImage.Width; j++)
                    {
                        distanceX = Math.Abs(j - (this.originalImage.Width / 2));
                        pointDistance = Math.Sqrt(Math.Pow(distanceX, 2) + Math.Pow(distanceY, 2));
                        if (pointDistance <= distance + (bandwidth / 2) && pointDistance >= distance - (bandwidth / 2))
                        {
                        }
                        else
                        {
                            ops.realR[j, i] *= 0;
                            ops.realG[j, i] *= 0;
                            ops.realB[j, i] *= 0;
                            ops.imaginaryR[j, i] *= 0;
                            ops.imaginaryG[j, i] *= 0;
                            ops.imaginaryB[j, i] *= 0;

                            red = this.originalImage.GetPixel(j, i).R * 0;
                            green = this.originalImage.GetPixel(j, i).G * 0;
                            blue = this.originalImage.GetPixel(j, i).B * 0;
                            this.originalImage.SetPixel(j, i, Color.FromArgb(red, green, blue));
                        }
                    }
                }

                return this.originalImage;
            }
            catch(Exception ex)
            {
                Logger.LogException(ex);
                throw;
            }
        }
    }
}
